package xml;

import java.io.*;

import java.net.URL;
import java.util.HashMap;
import java.util.List;

import logica.persistencia.DiagramaGraficablePersistente;
import logica.persistencia.DiagramaPersistente;
import logica.persistencia.ProyectoGraficablePersistente;
import logica.persistencia.ProyectoPersistente;

import org.jdom.*;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import vista.objetos.ProyectoGraficable;


public class XMLManager {
	
	private static XMLManager instance = new XMLManager();
	private Document documento = null;
	private SAXBuilder builder = null;
	private String path = null;
	static int identificador = 0;
	private HashMap<String, Object> objetosDiagramas = new HashMap<String, Object>();
	
	public XMLManager() {
		inicializar();
	}
	
	static public void clearXMLManager() {
		instance = null;
		identificador = 0;
		instance = new XMLManager();
		instance.objetosDiagramas = new HashMap<String, Object>();
	}
	
	public void clearDocument() {
		this.objetosDiagramas = new HashMap<String, Object>();
		inicializar();
	}
	
	public XMLManager (String xmlPath) {
		inicializar(xmlPath);
	}
	
	public static XMLManager getInstance() {
		return instance;
	}
	
	private void inicializar() {
		documento = new Document();
		crearBuilder();
	}
	
	private void inicializar(String xmlPath) {
		crearBuilder();
		abrirDocumentXML(xmlPath);
	}
	
	private void crearBuilder() {
		builder = new SAXBuilder(true); 
		builder.setFeature(ConstantesXml.FEATURE, true);
	}
	
	public Boolean abrirDocumentXML(String xmlPath) {
		try {
			documento = builder.build(xmlPath);

		
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		} catch (JDOMException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public void agregarElementoAObjetos (Element elementoEntidad) {
		//Element objetos = documento.getRootElement().getChild(ConstantesXml.ELEMENTO_DIAGRAMA).getChild(ConstantesXml.ELEMENTO_OBJETOS);
		Element objetos = documento.getRootElement().getChild(ConstantesXml.ELEMENTO_OBJETOS);
		if (objetos != null) {
			objetos.addContent(elementoEntidad);
		}
		
	}
	
	public void agregarElementoAElemento (Element padre, Element hijo) {
		padre.addContent(hijo);
	}
	
	public Boolean guardarXMLDocument(String path) {
		try {
			Format format = Format.getPrettyFormat();
			XMLOutputter out = new XMLOutputter(format);
	        FileWriter writer = new FileWriter(path);
	        out.output(documento, writer);
	        writer.flush();
	        writer.close();	
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	
	public Boolean guardarXMLDocument(Document doc, String path) {
		try {
			Format format = Format.getPrettyFormat();
			XMLOutputter out = new XMLOutputter(format);
	        FileWriter writer = new FileWriter(path);
	        out.output(doc, writer);
	        writer.flush();
	        writer.close();	
		} catch (FileNotFoundException e) {
			e.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		return true;
	}
	private void agregarSchema (String path) {
		
		URL url = Thread.currentThread().getContextClassLoader().getResource(path);
		String urlString = url.toString().toString();
		
		Element raiz = documento.getRootElement();
		Namespace xsi = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
		raiz.setAttribute("noNamespaceSchemaLocation", urlString, xsi);

	}
	
	public void agregarRaiz (Element raiz) {
		documento.setRootElement(raiz);
		this.agregarSchema(ConstantesXml.XML_SCHEMA_PATH);
	}
	
	public void agregarRaizGrafico (Element raiz) {
		documento.setRootElement(raiz);
		this.agregarSchema(ConstantesXml.XML_SCHEMA_REP_PATH);
	}
	
	
	public Element obtenerRaiz () {
		return documento.getRootElement();
	}
	public void agregarElementoARaiz (Element elemento) {
		documento.getRootElement().addContent(elemento);
	}
	
	public Element obtenerElementoConNombre (String nombreElemento) {
		Element elemento = this.obtenerRaiz().getChild(nombreElemento);
		return elemento;
	}
	
	public List<?> obtenerElementosConNombre (String nombreElemento) {
		Element raiz = this.obtenerRaiz();
		return raiz.getChildren(nombreElemento);
		
	}
	public String documentoToString () {
		Format format = Format.getPrettyFormat();  
		XMLOutputter xmloutputter = new XMLOutputter(format);
		String docStr = xmloutputter.outputString(documento);
		return docStr;
	}
	
	public void hashObjetoDiagramaConClave(String clave, Object valor) {
		objetosDiagramas.put(clave, valor);
	}
	
	public Object objtenerObjetoDiagramaConClave(String clave) {
		return objetosDiagramas.get(clave);
	}
	
	
	public void validar() {
		try {
			String xmlToString = documentoToString();
			StringReader reader = new StringReader(xmlToString);
			documento = builder.build(reader);
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	static public int getIdentificador() {
		return identificador++;
	}
	
	static public int getIdentificador(Object object) {
		return System.identityHashCode(object);
	}

	public void setPath(String path) {
		this.path = path;
	}
	
	public String getPath() {
		return this.path;
	}

	public String getComponentsFullPathWithName(String nombreDiagrama) {
		return this.path + File.separator + nombreDiagrama + ConstantesXml.XML_POSFIJO_MODELO + ConstantesXml.XML_EXTENSION;
	}
	
	public String getRepresentationFullPathWithName(String nombreDiagrama) {
		return this.path + nombreDiagrama + ConstantesXml.XML_POSFIJO_VISUAL + ConstantesXml.XML_EXTENSION;
	}


	public Boolean serializarDiagramaPersistente(DiagramaPersistente diagP) {
		String nombreDiagrama = diagP.getElementoDiagrama().getAttributeValue(ConstantesXml.ATRIBUTO_NOMBRE);
		String fullPath = XMLManager.getInstance().getComponentsFullPathWithName(nombreDiagrama);
		Boolean exito = XMLManager.getInstance().guardarXMLDocument(fullPath);
		XMLManager.getInstance().clearDocument();
		return exito;
	}
	
	public Boolean serializarDiagramaGraficoPersistente(DiagramaGraficablePersistente diagP) {
		String nombreDiagrama = diagP.getElementoDiagrama().getAttributeValue(ConstantesXml.ATRIBUTO_NOMBRE);
		String fullPath = XMLManager.getInstance().getRepresentationFullPathWithName(nombreDiagrama);
		Boolean exito = XMLManager.getInstance().guardarXMLDocument(fullPath);
		XMLManager.getInstance().clearDocument();
		return exito;
	}
	
	public Boolean hidratarDiagramaPersistente(DiagramaPersistente diagP) {
		String nombreDiagrama = diagP.getDiagrama().getNombre();	
		String fullPath = XMLManager.getInstance().getComponentsFullPathWithName(nombreDiagrama);
		return XMLManager.getInstance().abrirDocumentXML(fullPath);  
	}
	
	public Boolean serializar(ProyectoGraficable proyectoGraficable) {
		ProyectoPersistente proyectoPersistente = proyectoGraficable.getProyecto().getProyectoPersistente();
		ProyectoGraficablePersistente proyectoPersistenteG = proyectoGraficable.getProyectoPersistente();
		Boolean exitoModelo = proyectoPersistente.serializar();
		Boolean exitoGraficable = proyectoPersistenteG.serializar();
		return exitoModelo & exitoGraficable;
	}
	
	
	public Boolean hidratar(ProyectoGraficable proyectoGraficable) {
		ProyectoPersistente proyectoPersistente = proyectoGraficable.getProyecto().getProyectoPersistente();
		ProyectoGraficablePersistente proyectoPersistenteG = proyectoGraficable.getProyectoPersistente();
		Boolean exitoModelo = proyectoPersistente.hidratar();
		Boolean exitoGraficable = proyectoPersistenteG.hidratar();
		return exitoModelo & exitoGraficable;
	}

	public boolean hidratarDiagramaGraficoPersistente(
			DiagramaGraficablePersistente diagP) {
		
		String nombreDiagrama = diagP.getDiagramaGraficable().getDiagrama().getNombre();
		String fullPath = XMLManager.getInstance().getRepresentationFullPathWithName(nombreDiagrama);
		Boolean exito = XMLManager.getInstance().abrirDocumentXML(fullPath);
		return  exito;
	}

	public Object objtenerObjetoDiagramaRepConClave(String clave) {
		String _clave = clave + "rep";
		return objetosDiagramas.get(_clave);
	}
	public void hashObjetoDiagramaRepConClave(String clave, Object valor) {
		String _clave = clave + "rep";
		objetosDiagramas.put(_clave, valor);
	}

}